#include <actions.h>

/**
 * \file actions.cpp 
 * \author Ameen Jaber
 * \author Mohamad Noureddine
 * \author Mohamed Sabra
 * \brief Contains implementation of the actions in the grammar
*/

int tunit_initialize(TPGraph_t * graph) {
  //graph = new TPGraph_t(0);
  int root_id = add_vertex(*graph); // Add root node 
  (*graph)[root_id].op = root;
  return root_id;
}

void tunit_updateroot(TPGraph_t * graph, int rootid, int vid, int e_count) {
  if (vid != -1) {
    // Add a new edge to the node
    bool condition;
    TPGraph_t::edge_descriptor desc;

    tie(desc, condition) = add_edge(rootid, vid, *graph);
    (*graph)[desc].label = e_count; 
  }
}

int int_update(TPGraph_t * graph) {
  int vid = add_vertex(*graph); // create a new node for this integer variable
  (*graph)[vid].op = Integer; //  mark it as an integer
  return vid;
}

int lvalue_id(TPTable_t * table, std::string index) {
  int rid;
  TPTable_t::iterator it;
  // search for identifier
  it = (*table).find(index);
  if (it != (*table).end()) { // identifier found
    rid = (*table)[index];
  } else { // identifier not found
    rid = -1;
  }	
  return rid;
}

int additive_update(TPGraph_t * graph, int lid, int rid, int type) {
  if (graph == NULL) {
    std::cerr << "Graph is NULL\n";
    exit(-1);
  }

  // add a new node for the operator 
  int pid = add_vertex(*graph);
  // set the node's operator
  if (type == 1) { // + 
    (*graph)[pid].op = Plus;
  } else if (type == 2) { // -
    (*graph)[pid].op = Minus;
  }	

  // create edges and connect it to this node 
  bool condition;
  TPGraph_t::edge_descriptor e;
  tie(e, condition) = add_edge(pid, lid, *graph);
  (*graph)[e].label = 1; // first left neighboor
  tie(e, condition) = add_edge(pid, rid, *graph);
  (*graph)[e].label = 2; // second right neighbor

  return pid; // return the id of the operator node
}

int multiplicative_update(TPGraph_t * graph, int lid, int rid, int type) {
  if (graph == NULL) {
    std::cerr << "Graph is NULL\n";
    exit(-1);
  }

  // add a new node for the operator 
  int mid = add_vertex(*graph);
  // set the node's operator
  if (type == 1) { // *
    (*graph)[mid].op = Multiply;
  } else if (type == 2) { // /
    (*graph)[mid].op = Divide;
  } else if (type == 3) { // %
    (*graph)[mid].op = Modulo;
  }	

  // create edges and connect it to this node 
  bool condition;
  TPGraph_t::edge_descriptor e;
  tie(e, condition) = add_edge(mid, lid, *graph);
  (*graph)[e].label = 1; // first left neighboor
  tie(e, condition) = add_edge(mid, rid, *graph);
  (*graph)[e].label = 2; // second right neighbor

  return mid; // return the id of the operator node
}

int power_update(TPGraph_t * graph, int lid, int rid) {
  if (graph == NULL) {
    std::cerr << "Graph is NULL\n";
    exit(-1);
  }

  // add a new node for the operator 
  int mid = add_vertex(*graph);
  // set the node's operator
  (*graph)[mid].op = Exponent; // ^

  // create edges and connect it to this node 
  bool condition;
  TPGraph_t::edge_descriptor e;
  tie(e, condition) = add_edge(mid, lid, *graph);
  (*graph)[e].label = 1; // first left neighboor
  tie(e, condition) = add_edge(mid, rid, *graph);
  (*graph)[e].label = 2; // second right neighbor

  return mid; // return the id of the operator node
}

int cast_update(TPGraph_t * graph, int pid, int type) {
  if (graph == NULL) {
    std::cerr << "Graph is NULL\n";
    exit(-1);
  }

  // add a new node for the unary operator
  int uid = add_vertex(*graph);

  // switch on the type of the unary operator
  switch(type) {
  case 1:
    (*graph)[uid].op = uPlus;
    break;
  case 2: 
    (*graph)[uid].op = uMinus;
    break;
  case 3: 
    (*graph)[uid].op = uNot;
    break;
  default:
    break;
  }

  // create an edge for the expression and connect it to this node 
  bool condition; 
  TPGraph_t::edge_descriptor e;
  tie(e, condition) = add_edge(uid, pid, *graph);
  (*graph)[e].label = 0;

  return uid; // return the node id of the unary operator's node
}

int postfix_update(TPGraph_t * graph, int lid, int rid){
  if (graph == NULL) {
    std::cerr << "Graph is NULL\n";
    exit(-1);
  }

  // add a new node for the operator 
  int mid = add_vertex(*graph);
  (*graph)[mid].op = Access;

  // create edges and connect it to this node 
  bool condition;
  TPGraph_t::edge_descriptor e;
  tie(e, condition) = add_edge(mid, lid, *graph);
  (*graph)[e].label = 1; // first left neighboor
  tie(e, condition) = add_edge(mid, rid, *graph);
  (*graph)[e].label = 2; // second right neighbor

  return mid; // return the id of the operator node
}

int create_constant(TPGraph_t * graph, int val) {
  if (graph == NULL) {
    std::cerr << "Graph is NULL\n";
    exit(-1);
  }

  int cid = add_vertex(*graph);
  (*graph)[cid].op = Constant;
  (*graph)[cid].value = val; // assign the constant's value

  return cid; // returns the id of the created node
}

int assignment_update(TPGraph_t * graph, int lid, int rid) {
  if (graph == NULL) {
    std::cerr << "Graph is NULL\n";
    exit(-1);
  }
  int eid=add_vertex(*graph);
  (*graph)[eid].op = Assignment;

  // connect left and right values to the assignment op
  bool condition;
  TPGraph_t::edge_descriptor e;
  tie(e, condition) = add_edge(eid, lid, *graph);
  (*graph)[e].label = 1; // first left neighboor
  tie(e, condition) = add_edge(eid, rid, *graph);
  (*graph)[e].label = 2; // second right neighbor

  return eid; // return the id of the operator node
}

int relational_update(TPGraph_t * graph, int lid, int rid, TP_Op_t type) {
  if (graph == NULL) {
    std::cerr << "Graph is NULL\n";
    exit(-1);
  }
  int eid=add_vertex(*graph);
  (*graph)[eid].op = type;

  // connect left and right values to the relational op
  bool condition;
  TPGraph_t::edge_descriptor e;
  tie(e, condition) = add_edge(eid, lid, *graph);
  (*graph)[e].label = 1; // first left neighboor
  tie(e, condition) = add_edge(eid, rid, *graph);
  (*graph)[e].label = 2; // second right neighbor

  return eid; // return the id of the operator node
}

int  logical_and_update(TPGraph_t * graph, int lid, int rid) {
  if (graph == NULL) {
    std::cerr << "Graph is NULL\n";
    exit(-1);
  }
  int eid=add_vertex(*graph);
  (*graph)[eid].op = And;

  // connect left and right values to the relational op
  bool condition;
  TPGraph_t::edge_descriptor e;
  tie(e, condition) = add_edge(eid, lid, *graph);
  (*graph)[e].label = 1; // first left neighboor
  tie(e, condition) = add_edge(eid, rid, *graph);
  (*graph)[e].label = 2; // second right neighbor

  return eid; // return the id of the operator node
}

int  logical_or_update(TPGraph_t * graph, int lid, int rid) {
  if (graph == NULL) {
    std::cerr << "Graph is NULL\n";
    exit(-1);
  }
  int eid=add_vertex(*graph);
  (*graph)[eid].op = Or;

  // connect left and right values to the relational op
  bool condition;
  TPGraph_t::edge_descriptor e;
  tie(e, condition) = add_edge(eid, lid, *graph);
  (*graph)[e].label = 1; // first left neighboor
  tie(e, condition) = add_edge(eid, rid, *graph);
  (*graph)[e].label = 2; // second right neighbor

  return eid; // return the id of the operator node
}

int  equality_update(TPGraph_t * graph, int lid, int rid) {
  if (graph == NULL) {
    std::cerr << "Graph is NULL\n";
    exit(-1);
  }
  int eid=add_vertex(*graph);
  (*graph)[eid].op = Equal;

  // connect left and right values to the relational op
  bool condition;
  TPGraph_t::edge_descriptor e;
  tie(e, condition) = add_edge(eid, lid, *graph);
  (*graph)[e].label = 1; // first left neighboor
  tie(e, condition) = add_edge(eid, rid, *graph);
  (*graph)[e].label = 2; // second right neighbor

  return eid; // return the id of the operator node
}

int iterator_create(TPGraph_t * graph) {
  if (graph == NULL) {
    std::cerr << "Graph is NULL\n";
    exit(-1);
  }

  // create a node for the iterator expression
  int eid=add_vertex(*graph);
  (*graph)[eid].op = For;

  return eid;
}

int iterator_check(TPTable_t *  table, std::string index) {
  if (table == NULL) {
    std::cerr << "Table is NULL";
    exit(-1);
  }

  // check if iterator is in the table or not 
  int rid;
  TPTable_t::iterator it;
  // search for identifier
  it = (*table).find(index);
  if (it != (*table).end()) { // identifier found
    rid = (*table)[index];
  } else { // identifier not found
    rid = -1;
  }	
  return rid;
}

int iterator_connect(TPGraph_t * graph, int for_id, int i1_id, int count) {
  if (graph == NULL) {
    return -1;
  } 

  // create edge 
  bool condition;
  TPGraph_t::edge_descriptor e;
  tie(e, condition) = add_edge(for_id, i1_id, *graph);
  (*graph)[e].label = count; // first left neighboor
  return 1;
}

int conditional_create(TPGraph_t * graph, int type) {
  if (graph == NULL) {
    std::cerr << "Graph is NULL\n";
    exit(-1);
  }

  // create a node for the iterator expression
  int eid=add_vertex(*graph);

  switch(type) {
  case 1:
    (*graph)[eid].op = If; // if node
    break;
  case 2:
    (*graph)[eid].op = Then; // then node
    break;
  case 3:
    (*graph)[eid].op = Else; // else node
    break;
  default:
    break;
  }

  return eid;
}

int conditional_connect(TPGraph_t * graph, int if_id, int eid, int count) {
  if (graph == NULL) {
    return -1;
  } 

  // create edge 
  bool condition;
  TPGraph_t::edge_descriptor e;
  tie(e, condition) = add_edge(if_id, eid, *graph);
  (*graph)[e].label = count; // first left neighboor
  return 1;
}

int while_create(TPGraph_t * graph) {
  if (graph == NULL) {
    return -1;
  }

  // create the node 
  int eid=add_vertex(*graph);
  (*graph)[eid].op = While;

  return eid;
}

int while_connect(TPGraph_t * graph, int while_id, int eid, int count) {
  if (graph == NULL) {
    return -1;
  }
  
  bool condition;
  TPGraph_t::edge_descriptor e;
  tie(e, condition) = add_edge(while_id, eid, *graph);
  (*graph)[e].label = count;
  return 1;
}

int relation_set_update(TPGraph_t * graph, int lid, int type, int rid) {
  if (graph == NULL) {
    return -1;
  }

  // create node for the operation
  int nid = add_vertex(*graph);
  (*graph)[nid].op = (TP_Op_t)type;

  bool condition;
  TPGraph_t::edge_descriptor e;
  tie(e,condition) = add_edge(nid, lid, *graph); // left edge
  (*graph)[e].label = 1;
  tie(e,condition) = add_edge(nid, rid, *graph); //right edge
  (*graph)[e].label = 2;

  return nid; // return the id of the operator node
}

int scast_create(TPGraph_t * graph) {
  if (graph == NULL) {
    return -1;
  }
  
  int sid = add_vertex(*graph);
  (*graph)[sid].op = sNot;

  return sid; 
}

int scast_connect(TPGraph_t * graph, int uid, int vid) {
  if (graph == NULL) {
    return -1; 
  }

  bool condition;
  TPGraph_t::edge_descriptor e; 
  tie(e, condition) = add_edge(uid, vid, *graph);
  (*graph)[e].label = 1;

  return 1; // success
}

int spostfix_check(TPTable_t * table, TPGraph_t * graph, std::string index) {
  if (table == NULL || graph == NULL) {
    std::cerr << "Table is NULL";
    exit(-1);
  }
  
  // check if iterator is in the table or not 
  int rid;
  TPTable_t::iterator it;
  // search for identifier
  it = (*table).find(index);
  if (it != (*table).end()) { // identifier found
    rid = (*table)[index];
    if ((*graph)[rid].op != Set) {
      rid = -2;
    }
  } else { // identifier not found
    rid = -1;
  }	
  return rid;
}

int cardinality_update(TPGraph_t * graph, int vid) {
  if (graph == NULL) {
    std::cerr << "Graph is null!\n";
    exit(-1);
  }

  // create node for the cardinality operation
  int cid = add_vertex(*graph);
  (*graph)[cid].op = Cardinality;

  // connect the expression node to the cardinality node
  bool condition;
  TPGraph_t::edge_descriptor e;
  tie(e, condition) = add_edge(cid, vid, *graph);
  (*graph)[e].label = 1;

  return cid; // return the id of the cardinality node   
}

int custom_update(TPGraph_t * graph) {
  int vid = add_vertex(*graph); // create a new node for this custom variable
  (*graph)[vid].op = Custom; //  mark it as an custom
  return vid;
}
